/* TCSS 590A - Cybernetics and Systems
 * Group Project - Modeling
 * By Barry Jones, Naren Meadem, Michael Pitts, Yuan Tao
 * Fall 2012, University of Washington, Tacoma
 */
package control;

import java.io.FileNotFoundException;

import model.Model;
import model.delayed.Account;
import model.delayed.BasicAccount;
import model.delayed.DebtableAccount;
import model.delayed.DelayedModel;
import model.delayed.Generator;
import model.delayed.Loan;
import model.delayed.LongtermReturnInvestment;
import model.delayed.ModelController;
import model.delayed.ShortTermReturnInvestment;
import model.delayed.SimpleGenerator;
import model.delayed.SimpleModelController;
import model.delayed.savings;
import model.delayed.logistical.LogisticalController;
import model.delayed.strategic.OverController;
import model.delayed.strategic.StratCon;
import model.delayed.strategic.StrategicController;
import model.delayed.tactical.NegotiableGenerator;
import model.delayed.tactical.RareNegGenerator;
import model.delayed.tactical.RareShouldNegotiate;
import model.delayed.tactical.RareShouldNegotiate.NumFinder;
import model.delayed.tactical.TacticalController;

import output.OutPut;
import output.OutPutStream;

/**
 * This starts the simulation, using the command prompts to set up the model and
 * define the output.
 * @author Michael Pitts
 * @version Oct 30, 2012
 */
public class Starter
{

	/**
	 * Starts the whole simulation.
	 * @param args the command line arguments.
	 * @throws FileNotFoundException if a file is indicated for output but the indicated
	 * file can not be found.
	 */
	public static void main(final String[] args) throws FileNotFoundException
	{
		//final Model model = new SimplestModel(0.08,1000,300, 400, new OutPutStream(System.out));
		final Model model = getCurrentModel();
		while (model.runOne());
	}

	private static Model getSimpleDelayedModel()
	{
		final Account saving = new BasicAccount(1000, 0.08);
		final Account checking = new DebtableAccount(100, 0.12, -1000);
		final Account[] accounts = {checking, saving};
		final Generator wage = new SimpleGenerator(300);
		final Generator cost = new SimpleGenerator(-50);
		final ModelController control = new SimpleModelController(saving, checking);
		final OutPut out = new OutPutStream(System.out);
		out.out(new String[] {"Time", "Check", "Save"});
		out.newLine();
		return new DelayedModel(accounts, wage, cost, control, 12, out);
	}
	
	private static Model getCurrentModel()
	{
		final OutPut output = new OutPutStream(System.out);
		output.out(getHeaders());
		output.newLine();
		final ModelController strategic = getStrategicController();
		final Account[] accounts = getAccounts();
		final ModelController logistical = getLogisticalController(accounts);
		final NegotiableGenerator wage_gen = getWageGen(100, 10);
		final NegotiableGenerator cost_gen = getCostGen(-50, 10);
		final ModelController tactical = 
				getTacticalController((StrategicController )strategic, 
						wage_gen, cost_gen, 10, 10, output);
		final ModelController over_control = 
				new OverController(strategic, tactical, logistical);
		final int max = 180;
		return new DelayedModel(accounts, wage_gen, cost_gen, over_control, max, output);
	}

	/**
	 * Generates a new TacticalController for the model.
	 * @param strategic is the StrategicController for the model.
	 * @param wage_gen is the wage Generator for the model.
	 * @param cost_gen is the cost Generator for the model.
	 * @param wage_wait is how long the wage negotiatior should expect negotiations
	 * to take.
	 * @param cost_wait is how long the cost negotiatior should expect negotiations
	 * to take.
	 * @return a new ModelController that will handle logistical Events.
	 * @author Michael Pitts
	 */
	private static ModelController getTacticalController(
			final StrategicController strategic, 
			final NegotiableGenerator wage_gen,
			final NegotiableGenerator cost_gen,
			final int wage_wait, final int cost_wait, final OutPut the_out)
	{
		final NumFinder wage_finder = RareShouldNegotiate.getWageFinder(strategic);
		final NumFinder cost_finder = RareShouldNegotiate.getCostFinder(strategic);
		final RareShouldNegotiate wage_neg = 
				new RareShouldNegotiate(wage_finder, wage_wait);
		final RareShouldNegotiate cost_neg = 
				new RareShouldNegotiate(cost_finder, cost_wait);
		return new TacticalController(wage_gen, wage_neg, cost_gen, cost_neg, the_out);
	}

	/**
	 * Gets the Generator that will generate cost Events for the Model.
	 * @return a new NegotiableGenerator.
	 * @author Michael Pitts
	 */
	private static NegotiableGenerator getCostGen(final int the_max, 
			final int the_wait)
	{
		return new RareNegGenerator(the_wait){
			@Override public long generate(long the_time)
			{return Math.round(Math.random() * the_max);}

			@Override public int delay(long the_time) {return 0;}
		};
	
	}

	/**
	 * Gets the Generator that will generate wage Events for the Model.
	 * @param the_max is the maximum wage possible.
	 * @param the_wait is how long between successful negotiations.
	 * @return a new NegotiableGenerator.
	 * @author Michael Pitts
	 */
	private static NegotiableGenerator getWageGen(final int the_max, 
			final int the_wait)
	{
		return new RareNegGenerator(the_wait){
				@Override public long generate(long the_time)
				{ return (int) Math.round(Math.random()*the_max);}

				@Override public int delay(long the_time) {return 0;}
			};
	}

	/**
	 * Gets the StrategicController for the Model.
	 * @return a new ModelController that will handle strategic Events.
	 * @author Barry Jones
	 */
	private static ModelController getStrategicController()
	{
		
		return new StratCon();
	}

	/**
	 * Gets the LogisticalController for the Model.
	 * @param the_accounts
	 * @return a new ModelController that will handle the logistical Events.
	 * @author Naren Meadem
	 */
	private static ModelController getLogisticalController(
			final Account[] the_accounts)
	{
		
		return new LogisticalController((Loan)the_accounts[4], (LongtermReturnInvestment)the_accounts[2], (ShortTermReturnInvestment)the_accounts[3], (savings)the_accounts[1],(BasicAccount)the_accounts[0]);
	}

	/**
	 * Gets the Account objects for the Model.
	 * @return an array of type Account[].
	 * @author Naren Meadem
	 */
	private static Account[] getAccounts()
	{
		return new Account[] {new BasicAccount(50, 0, null), new savings(4000, 0.0002 ,null , 6, 242), new LongtermReturnInvestment(2000, 40, 5, 50, 3500, 600, 0, 2, 0, 10), new ShortTermReturnInvestment(2000, 40, 3, 50, 3000, 400, 25, 3, 0, 5), new Loan(1000,20,2,0.0)};
	}
	
	/**
	 * Gets the headers for the model data.
	 * @return a new String array with each entry as the name of an Account.
	 * @author Naren Meadem
	 */
	private static String[] getHeaders()
	{
		return new String[] {"Time", "check", "save", "Long", "Short", "Loan" }; 
	}
}
